// Array 是一个典型的泛型
const n: Array<number> = [1, 2, 3]
const s: Array<string> = ["1", "2", "3"]

// 配合 Union 可以方便实现类似 Rust 中的 Option 和 Result trait
type Option<T> = T | null
type Result<T> = T | Error


// 定义泛型函数
// 方式 1：经典版
function log1<T>(msg: string, v: T): T {
    console.log(`${msg} ${JSON.stringify(v)}`)
    return v
}
// 方式 2：函数作为常量
const log2 = function <T>(msg: string, v: T): T {
    console.log(`${msg} ${JSON.stringify(v)}`)
    return v
}
// 方式 3：箭头函数
const log3 = <T>(msg: string, v: T): T => {
    console.log(`${msg} ${JSON.stringify(v)}`)
    return v
}
// 方式 4：函数类型
type LogFunc = <T>(msg: string, v: T) => T;
const log4: LogFunc = (msg, v) => {
    console.log(`${msg} ${JSON.stringify(v)}`)
    return v
}

// 泛型用于函数式编程示例: 找到 elems 中第一个满足 judge 条件的值
const findFirst = <T>(elems: Array<T>, judge: (e: T) => boolean): Option<T> => {
    for (let e of elems) {
        if (judge(e)) {
            return e
        }
    }
    return null
}

// keyof 可以得到该数据对应类型的所有 field 值构成一个类型（类型 = 值的集合）
const v = { length: 10 };
const prop = <T>(v: T, key: keyof T) => v[key]
console.log(prop(v, "length"))    // 10
console.log(prop(1, "toString"))  // [Function: toString]
console.log(1["toString"])        // [Function: toString]


// Pick 和 Readonly
// Pick: 由 T 中的某些 field 组成一个新类型
// type Pick<T, K extends keyof T> = {
//     [P in K]: T[P]
// }
// Readonly: 将 T 中所有 field 改为只读生成一个新类型
// type Readonly<T> = {
//     readonly [P in keyof T] : T[P]
// }

type Configs = {
    material: string;
    backlight: boolean;
}
type PickConfigs = Pick<Configs, "material">;  // 等价于 { material : string } 类型
type ReadonlyConfigs = Readonly<Configs>;  
// 等价于 
// { 
//    readonly material: string; 
//    readonly backlight: boolean; 
// } 
// 类型
let c: ReadonlyConfigs = {
    material: "carbon",
    backlight: true
};
// c.material = "wood"  // error
console.log(c)

// 泛型运算
// 1. extends
// 2. keyof
// 3. in
// 4. ? :
// 5. never


// 应用：提取对象中指定类型的 key
type PickValues<T,V> = { [ P in keyof T as T[P] extends V ? P : never ] : T[P] }
function myFunction<T>(object: T, key: keyof PickValues<T,string> ){
    // ...
}

myFunction({keyA: "hello", keyB: 123}, "keyA")  // OK
// myFunction({keyA: "hello", keyB: 123}, "keyB")  // error: `keyB` must be of type string but is number


// 应用：提取对象中去除指定类型后的 key
type OmitValues<T,V> = { [ P in keyof T as T[P] extends V ? never : P ] : T[P] }
function yourFunction<T>(object: T, key: keyof OmitValues<T,string> ){
    // ...
}

// yourFunction({keyA: "hello", keyB: 123}, "keyA")  // error: `keyA` cannot be of type string but it is
yourFunction({keyA: "hello", keyB: 123}, "keyB")  // OK


// 应用：提取对象中可选类型的 key
// 利用以下事实：实际上可选参数（arg?: T） 等价于给它的原始类型 Union 一个 undefined（arg: T|undefined）
type OptionalValues<T> = { [P in keyof T as undefined extends T[P] ? P: never]: T[P] }

// 应用：提取对象中必选类型的 key
type RequiredValues<T> = { [P in keyof T as undefined extends T[P] ? never: P]: T[P] }



// 练习：将指定 T 中指定 field K 转换为新的类型 F，并且 readonly，从而生成一个新的复合类型
// 1. 从 T 中找到 field K，将其转换为 F 类型，并 Readonly，得到复合类型 T1
// 2. 从 T 中找到所有非 K 的 field，组成一个新的复合类型 T2
// 3. 将两者 intersect
type ReplaceAll<T, F> = {
    [P in keyof T]: F
}
type T1<T, K extends keyof T, F> = Readonly<ReplaceAll<Pick<T, K>, F>>;
type T2<T, K extends keyof T> = Omit<T, K>;
type Replace<T, K extends keyof T, F> = T1<T, K, F> & T2<T, K>;

// 测试
type HitCountOptions = Replace<Configs, "backlight", string>

const hitCount : HitCountOptions = {
    material : "material",
    backlight : "OK"
}
console.log(hitCount.material.length) // 8
// hitCount.backlight = "headlight" // compile error